﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace Tools.MAME
{
    ///-------------------------------------------------------------------------------------------------
    /// <summary> Class used to load CatVer.Ini file. </summary>
    ///-------------------------------------------------------------------------------------------------
    public class CatVerList : IEnumerable<CatVerItemInfo>
    {
        /// <summary> Constant used to signify an unclassified game. Matches what's used in the actual CatVer file. </summary>
        public const String NOT_CLASSIFIED = "Not Classified";

        private enum IniSection
        {
            Category,
            Version,
            None
        }

        private const String MATURE_VALUE = "*Mature*";
        private const String INI_CATEGORY_SECTION = "[Category]";
        private const String INI_VERSION_ADDED_SECTION = "[VerAdded]";

        private Dictionary<String, CatVerItemInfo> m_data = new Dictionary<string, CatVerItemInfo>(StringComparer.OrdinalIgnoreCase);

        #region Constructor(s)

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Default constructor. </summary>
        ///-------------------------------------------------------------------------------------------------
        public CatVerList()
        { }

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Constructor. </summary>
        /// <param name="file"> The file to load from. </param>
        ///-------------------------------------------------------------------------------------------------
        public CatVerList(String file)
        {
            Load(file);
        }

        #endregion

        #region Public

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Loads the CatVer.ini file. </summary>
        /// <exception cref="ArgumentNullException">   Thrown when one or more required arguments are
        /// null. </exception>
        /// <exception cref="FileNotFoundException">   Thrown when the requested file is not present. </exception>
        /// <exception cref="Exception">               Thrown when an exception error condition occurs. </exception>
        /// <exception cref="NotImplementedException"> Thrown when the requested operation is
        /// unimplemented. </exception>
        /// <param name="filepath"> The filepath to load. </param>
        ///-------------------------------------------------------------------------------------------------
        public void Load(String filepath)
        {
            if (filepath == null)
                throw new ArgumentNullException("filepath");

            if (!File.Exists(filepath))
                throw new FileNotFoundException("Unable to locate specified file.", filepath);

            m_data.Clear();

            using (StreamReader sr = new StreamReader(filepath))
            {
                IniSection currentSection = IniSection.None;

                String line = null;
                while ((line = sr.ReadLine()) != null)
                {
                    line = line.Trim();
                    if ((line.Length > 0) && (!line.StartsWith(";;", StringComparison.OrdinalIgnoreCase)))
                    {
                        if (line.Equals(INI_CATEGORY_SECTION, StringComparison.OrdinalIgnoreCase))
                            currentSection = IniSection.Category;
                        else if (line.Equals(INI_VERSION_ADDED_SECTION, StringComparison.OrdinalIgnoreCase))
                            currentSection = IniSection.Version;
                        else if (currentSection == IniSection.None)
                            throw new Exception(String.Format("Invalid line [{0}] found in ini file.", line));
                        else
                        {
                            String[] lineData = line.Split('=');
                            if (lineData.Length != 2)
                                throw new Exception(String.Format("Invalid line [{0}] found in ini file.", line));

                            String romName = lineData[0].Trim();

                            if (currentSection == IniSection.Category)
                            {
                                String categoryData = lineData[1].Trim();

                                // Determine if game is mature, and if so, adjust value accordingly.
                                bool isMature;
                                if (categoryData.EndsWith(MATURE_VALUE, StringComparison.OrdinalIgnoreCase))
                                {
                                    categoryData = Regex.Replace(categoryData, Regex.Escape(MATURE_VALUE), "", RegexOptions.IgnoreCase).Trim();
                                    isMature = true;
                                }
                                else
                                    isMature = false;

                                if (categoryData.Contains('/'))
                                {
                                    // Process category/sub-category.
                                    String[] categorySplit = categoryData.Split('/');
                                    if (categorySplit.Length != 2)
                                        throw new Exception(String.Format("Invalid category info [{0}].", categoryData));

                                    m_data.Add(romName, new CatVerItemInfo(romName, categorySplit[0].Trim(), categorySplit[1].Trim(), isMature));
                                }
                                else
                                    m_data.Add(romName, new CatVerItemInfo(romName, categoryData, null, isMature));
                            }
                            else if (currentSection == IniSection.Version)
                            {
                                // Assumes that category info has already been loaded.
                                m_data[romName].VersionAdded = lineData[1].Trim();
                            }
                            else
                                throw new NotImplementedException(String.Format("Support for [{0}] ini section has not been implemented.", currentSection.ToString()));
                        }
                    }
                } // End while loop.
            }
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Indexer to get items within this collection using array index syntax. </summary>
        /// <param name="key"> The key. </param>
        /// <returns> The indexed item. </returns>
        ///-------------------------------------------------------------------------------------------------
        public CatVerItemInfo this[String key]
        {
            get
            {
                CatVerItemInfo itemInfo;
                if (m_data.TryGetValue(key, out itemInfo))
                    return itemInfo;
                else
                    return null;
            }
        }

        #endregion

        #region IEnumerable<ItemInfo> Implementation

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Gets the enumerator. </summary>
        /// <returns> The enumerator. </returns>
        ///-------------------------------------------------------------------------------------------------
        public IEnumerator<CatVerItemInfo> GetEnumerator()
        {
            return m_data.Values.GetEnumerator();
        }

        ///-------------------------------------------------------------------------------------------------
        /// <summary> Gets the enumerator. </summary>
        /// <returns> The enumerator. </returns>
        ///-------------------------------------------------------------------------------------------------
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return m_data.Values.GetEnumerator();
        }

        #endregion
    }
}
